﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using System.Threading.Tasks;
using Windows.Phone.Media.Capture;
using System.Windows.Input;
using System.Threading;
using System.Windows.Shapes;
using System.Windows.Media;
using Microsoft.Devices;

namespace Typography
{
    public partial class CaptureLense : PhoneApplicationPage
    {

        // Constants
        private readonly Windows.Foundation.Size DefaultCameraResolution =
            new Windows.Foundation.Size(640, 480);

        private ProgressIndicator _progressIndicator = new ProgressIndicator();
        private Typography.DataContext _dataContext = Typography.DataContext.Singleton;
        private Semaphore _focusSemaphore = new Semaphore(1, 1);
        private Windows.Foundation.Size _focusRegionSize = new Windows.Foundation.Size(80, 80);
        private SolidColorBrush _notFocusedBrush = new SolidColorBrush(Colors.Red);
        private SolidColorBrush _focusedBrush = new SolidColorBrush(Colors.Green);
        private bool _manuallyFocused = false;

        private CameraSensorLocation _sensorLocation = CameraSensorLocation.Back;

        public CaptureLense()
        {
            InitializeComponent();

            VideoCanvas.Tap += new EventHandler<GestureEventArgs>(videoCanvas_Tap);

            DataContext = _dataContext;

            _progressIndicator.IsIndeterminate = true;
        }

        /// <summary>
        /// If camera has not been initialized when navigating to this page, initialization
        /// will be started asynchronously in this method. Once initialization has been
        /// completed the camera will be set as a source to the VideoBrush element
        /// declared in XAML. On-screen controls are enabled when camera has been initialized.
        /// </summary>
        protected async override void OnNavigatedTo(NavigationEventArgs e)
        {
            
            if (_dataContext.Device == null)
            {
                ShowProgress("Initializing camera...");
                await InitializeCamera(_sensorLocation);
                HideProgress();
            }

            videoBrush.RelativeTransform = new CompositeTransform()
            {
                CenterX = 0.5,
                CenterY = 0.5,
                Rotation = _dataContext.Device.SensorLocation == CameraSensorLocation.Back ?
                           _dataContext.Device.SensorRotationInDegrees :
                         -_dataContext.Device.SensorRotationInDegrees
            };

            videoBrush.SetSource(_dataContext.Device);

            //overlayComboBox.Opacity = 1;

            //SetScreenButtonsEnabled(true);
            //SetCameraButtonsEnabled(true);

            base.OnNavigatedTo(e);
        }

        private async Task InitializeCamera(CameraSensorLocation sensorLocation)
        {
            // Find out the largest capture resolution available on device
            IReadOnlyList<Windows.Foundation.Size> availableResolutions =
                PhotoCaptureDevice.GetAvailableCaptureResolutions(sensorLocation);

            Windows.Foundation.Size captureResolution = new Windows.Foundation.Size(0, 0);

            for (int i = 0; i < availableResolutions.Count; ++i)
            {
                if (captureResolution.Width < availableResolutions[i].Width)
                {
                    //Debug.WriteLine("MainPage.InitializeCamera(): New capture resolution: " + availableResolutions[i]);
                    captureResolution = availableResolutions[i];
                }
            }

            PhotoCaptureDevice device =
                await PhotoCaptureDevice.OpenAsync(sensorLocation, DefaultCameraResolution);

            await device.SetPreviewResolutionAsync(DefaultCameraResolution);
            await device.SetCaptureResolutionAsync(captureResolution);

            device.SetProperty(KnownCameraGeneralProperties.EncodeWithOrientation,
                          device.SensorLocation == CameraSensorLocation.Back ?
                          device.SensorRotationInDegrees : -device.SensorRotationInDegrees);

            _dataContext.Device = device;
        }

        /// <summary>
        /// Set autofocus area to tap location and refocus.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void videoCanvas_Tap(object sender, GestureEventArgs e)
        {
            System.Windows.Point uiTapPoint = e.GetPosition(VideoCanvas);

            if (PhotoCaptureDevice.IsFocusRegionSupported(_dataContext.Device.SensorLocation) && _focusSemaphore.WaitOne(0))
            {
                // Get tap coordinates as a foundation point
                Windows.Foundation.Point tapPoint = new Windows.Foundation.Point(uiTapPoint.X, uiTapPoint.Y);

                double xRatio = VideoCanvas.ActualHeight / _dataContext.Device.PreviewResolution.Width;
                double yRatio = VideoCanvas.ActualWidth / _dataContext.Device.PreviewResolution.Height;

                // adjust to center focus on the tap point
                Windows.Foundation.Point displayOrigin = new Windows.Foundation.Point(
                            tapPoint.Y - _focusRegionSize.Width / 2,
                            (VideoCanvas.ActualWidth - tapPoint.X) - _focusRegionSize.Height / 2);

                // adjust for resolution difference between preview image and the canvas
                Windows.Foundation.Point viewFinderOrigin = new Windows.Foundation.Point(displayOrigin.X / xRatio, displayOrigin.Y / yRatio);
                Windows.Foundation.Rect focusrect = new Windows.Foundation.Rect(viewFinderOrigin, _focusRegionSize);

                // clip to preview resolution
                Windows.Foundation.Rect viewPortRect = new Windows.Foundation.Rect(0, 0, _dataContext.Device.PreviewResolution.Width, _dataContext.Device.PreviewResolution.Height);
                focusrect.Intersect(viewPortRect);

                _dataContext.Device.FocusRegion = focusrect;

                // show a focus indicator
                FocusIndicator.SetValue(Shape.StrokeProperty, _notFocusedBrush);
                FocusIndicator.SetValue(Canvas.LeftProperty, uiTapPoint.X - _focusRegionSize.Width / 2);
                FocusIndicator.SetValue(Canvas.TopProperty, uiTapPoint.Y - _focusRegionSize.Height / 2);
                FocusIndicator.SetValue(Canvas.VisibilityProperty, Visibility.Visible);

                CameraFocusStatus status = await _dataContext.Device.FocusAsync();

                if (status == CameraFocusStatus.Locked)
                {
                    FocusIndicator.SetValue(Shape.StrokeProperty, _focusedBrush);
                    _manuallyFocused = true;
                    _dataContext.Device.SetProperty(KnownCameraPhotoProperties.LockedAutoFocusParameters,
                        AutoFocusParameters.Exposure & AutoFocusParameters.Focus & AutoFocusParameters.WhiteBalance);
                }
                else
                {
                    _manuallyFocused = false;
                    _dataContext.Device.SetProperty(KnownCameraPhotoProperties.LockedAutoFocusParameters, AutoFocusParameters.None);
                }

                _focusSemaphore.Release();
            }
        }

        /// <summary>
        /// Starts displaying progress indicator.
        /// </summary>
        /// <param name="msg">Text message to display.</param>
        private void ShowProgress(String msg)
        {
            _progressIndicator.Text = msg;
            _progressIndicator.IsVisible = true;

            SystemTray.SetProgressIndicator(this, _progressIndicator);
        }

        /// <summary>
        /// Stops displaying progress indicator.
        /// </summary>
        private void HideProgress()
        {
            _progressIndicator.IsVisible = false;

            SystemTray.SetProgressIndicator(this, _progressIndicator);
        }
    }
}